#include "cybertron/network/SocketUdpCommon.hpp"
#include "cybertron/core/Log.hpp"
#include "cybertron/core/UtilCRC.hpp"

CYBERTRON_BEGIN

const std::string& SocketUdpCommon::messageHeader()
{
	static const std::string messageHeader = { (char)0xA5, (char)0xA5 };
	return messageHeader;
}

const std::string& SocketUdpCommon::messageFooter()
{
	static const std::string messageFooter = { (char)0x5A, (char)0x5A };
	return messageFooter;
}

std::string SocketUdpCommon::encodeSimpleMessage(const std::string& messageContent)
{
	if (messageContent.size() > UDP_MAX_FRAGMENT_CONTENT_SIZE) {
		logError("Message too bigger, not supported by encodeSimpleMessage!");
		return "";
	}
	std::string encodedMessage = messageHeader() + messageContent + messageFooter();
	return encodedMessage;
}

std::string SocketUdpCommon::decodeSimpleMessage(const std::array<char, UDP_MAX_FRAGMENT_SIZE>& buffer, std::uint16_t messageSize)
{
	if (!checkHeaderAndFooter(buffer, messageSize)) {
		logError("Invalid Message in decodeSimpleMessage!");
		return "";
	}
	auto messageOffset = messageHeader().size();
	auto footerOffset = messageSize - messageFooter().size();
	std::string message(buffer.begin() + messageOffset, buffer.begin() + footerOffset);
	return message;
}

bool SocketUdpCommon::checkHeaderAndFooter(const std::array<char, UDP_MAX_FRAGMENT_SIZE>& buffer, std::uint16_t messageSize)
{
	const auto& header = messageHeader();
	const auto& footer = messageFooter();
	const auto headerSize = header.size();
	const auto footerSize = footer.size();
	if (messageSize > UDP_MAX_FRAGMENT_SIZE || messageSize <= headerSize + footerSize) {
		return false;
	}
	bool bValid = true;
	for (auto i = 0; i < headerSize; i++) {
		if (buffer[i] != header[i]) {
			bValid = false;
			break;
		}
	}
	auto footerOffset = messageSize - footerSize;
	for (auto i = 0; i < footerSize; i++) {
		if (buffer[footerOffset + i] != footer[i]) {
			bValid = false;
			break;
		}
	}
	return bValid;
}

std::vector<std::string> SocketUdpCommon::encodeMessage(const std::string& messageContent, std::uint16_t sequenceId)
{
	auto contentWithCRCSize = messageContent.size() + 4;
	std::vector<std::string> fragments;
	if (contentWithCRCSize > UDP_MAX_MESSAGE_SIZE) {
		logError("Message tooooo long, not supported currently!");
		return fragments;
	}

	std::uint16_t fragmentCount = (std::uint16_t)(contentWithCRCSize / UDP_MAX_FRAGMENT_CONTENT_SIZE);
	if (contentWithCRCSize % UDP_MAX_FRAGMENT_CONTENT_SIZE != 0) {
		fragmentCount++;
	}
	size_t contentOffset = 0;
	for (std::uint16_t i = 0; i < fragmentCount; i++) {
		std::string fragment;
		fragment.reserve(8);
		fragment.append(messageHeader());
		fragment.append((char*)&sequenceId, 2);
		fragment.append((char*)&fragmentCount, 2);
		fragment.append((char*)&i, 2);
		if (i < fragmentCount - 1) {
			std::string fragmentContent = messageContent.substr(contentOffset, UDP_MAX_FRAGMENT_CONTENT_SIZE);
			fragment.append(fragmentContent);
			contentOffset += fragmentContent.size();
		}
		else {
			if (contentOffset < messageContent.size()) {
				fragment.append(messageContent.substr(contentOffset));
			}
			std::uint32_t crcValue = UtilCRC::crc32_encode(messageContent.c_str(), messageContent.size());
			fragment.append((char*)&crcValue, 4);
		}
		fragment.append(messageFooter());
		fragments.emplace_back(std::move(fragment));
	}
	return fragments;
}

bool SocketUdpCommon::decodeMessage(const std::array<char, UDP_MAX_FRAGMENT_SIZE>& buffer, std::uint16_t messageSize, UDPMessageFragment& messageFragment)
{
	if (!checkHeaderAndFooter(buffer, messageSize)) {
		logError("Invalid Message in decodeSimpleMessage!");
		return false;
	}
	auto messageOffset = messageHeader().size();
	messageFragment.SequenceId = *(std::uint16_t*)(&buffer[messageOffset]);
	messageOffset += sizeof(std::uint16_t);
	messageFragment.FragmentCount = *(std::uint16_t*)(&buffer[messageOffset]);
	messageOffset += sizeof(std::uint16_t);
	messageFragment.FragmentId = *(std::uint16_t*)(&buffer[messageOffset]);
	if (messageFragment.FragmentId >= messageFragment.FragmentCount) {
		logError("Message Fragment is invalid.");
		return false;
	}
	messageOffset += sizeof(std::uint16_t);
	auto footerOffset = messageSize - messageFooter().size();
	messageFragment.Fragment = std::string(buffer.begin() + messageOffset, buffer.begin() + footerOffset);
	return true;
}

bool UDPMessage::isMessageReady() const
{
	auto size = MessageFragments.size();
	if (size <= 0) {
		return false;
	}
	return FragmentCount == size;
}

bool UDPMessage::isMessageOverdue() const
{
	auto timespan = std::chrono::high_resolution_clock::now() - CreateTime;
	return timespan.count() > UDP_MESSAGE_EXPIRE_TIME_MS * 1e6;
}

std::string UDPMessage::decodeMessage() const
{
	if (!isMessageReady()) {
		return "";
	}
	std::string result;
	int index = 0;
	for (const auto& keyValue : MessageFragments) {
		if (keyValue.first != index) {
			logError("Message is invalid for decode.");
			return "";
		}
		const auto& fragment = keyValue.second.Fragment;
		if (index < FragmentCount - 1) {
			result.append(fragment);
		}
		else {
			auto crcOffset = fragment.size() - 4;
			result.append(fragment.substr(0, crcOffset));
			std::uint32_t expectedCRC = *((std::uint32_t*)(fragment.c_str() + crcOffset));
			std::uint32_t actualCRC = UtilCRC::crc32_encode(result.c_str(), result.size());
			if (expectedCRC != actualCRC) {
				logError("CRC value is incorrect for the message.");
				return "";
			}
		}
		index++;
	}
	return std::move(result);
}

bool UDPMessage::addMessageFragment(UDPMessageFragment& messageFragment)
{
	auto fragmentId = messageFragment.FragmentId;
	if (MessageFragments.empty()) {
		SequenceId = messageFragment.SequenceId;
		FragmentCount = messageFragment.FragmentCount;
	}
	else if (MessageFragments.find(fragmentId) != MessageFragments.end()) {
		return false;
	}
	//logDebug("Received Message Fragment: %d, %d, %d\r\n\r\n", SequenceId, FragmentCount, fragmentId);
	MessageFragments.emplace(fragmentId, std::move(messageFragment));
	return true;
}

CYBERTRON_END
